package net.lovesrc.allin.biz.lbc;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.zhouzhou.fastjava.BizException;
import com.zhouzhou.fastjava.http.FilePart;
import com.zhouzhou.fastjava.http.MyHttpRequest;
import com.zhouzhou.fastjava.util.BigDecimalKit;
import com.zhouzhou.fastjava.util.MoneyKit;
import com.zhouzhou.fastjava.util.StringKit;
import net.lovesrc.allin.biz.jietu.JieTuService;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.math.BigDecimal;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * @author clg
 * Created at 16:28 2019/2/25
 * @since 1.1
 */
@Service
public class LbcService extends BaseLbcService {


    @Autowired
    private JieTuService jieTuService;

    public static final File JIE_TU_DIR = new File("d:/tmp/images/jietu");

    public void profits(int size, Proxy proxy, boolean desc) {
        String descStr = desc ? "desc" : "asc";
        List<JSONObject> rows = queryForRows(jdbcTemplate, "select mobile from t_lbc_account where worked = 1 " +
                        " and profitTime < date(now()) and nextProfitTime > date(now()) and nextProfitTime <= now() " +
                        " order by  nextProfitTime " + descStr + " limit ?",
                size
        );
        for (JSONObject row : rows) {
            String mobile = row.getString("mobile");
            profitAndSyncData(mobile, proxy);
        }
    }

    public void profits(int size, Proxy proxy) {
        profits(size, proxy, false);
    }

    public void profitAndSyncData(String mobile, Proxy proxy) {
        JSONObject row = getAndCheckRow(mobile);
        String profitStr = get2Text(mobile, "https://www.bluebeicoin.com/myminers/get_profit", proxy);
        JSONObject sets = new JSONObject()
                .fluentPut("profitTime", new Date())
                .fluentPut("profitResText", profitStr)
                .fluentPut("profitResStatus", profitStr.contains("success") ? "success" : "fail");
        updateRow(jdbcTemplate, "t_lbc_account", sets, "mobile = ?", row.getString("mobile"));
        getUserInfo(mobile, proxy);
    }

    public LbcUser getUserInfo(String mobile, Proxy proxy) {
        String userStr = get2Text(mobile, "https://www.bluebeicoin.com/user", proxy);
        Elements userElements = Jsoup.parse(userStr)
                .select("div.left_top2 p");
        int startLevel = NumberUtils.toInt(StringKit.extract(userElements.get(0).text(), "星级：(\\d)级"));
        double goldAccount = NumberUtils.toDouble(StringKit.extract(userElements.get(2).text(), "金币账户：([0-9\\.]+)"));
        double silverAccount = NumberUtils.toDouble(StringKit.extract(userElements.get(3).text(), "银币账户：([0-9\\.]+)"));
        JSONObject sets = new JSONObject()
                .fluentPut("startLevel", startLevel)
                .fluentPut("goldAccount", goldAccount)
                .fluentPut("silverAccount", silverAccount);
        updateRow(jdbcTemplate, "t_lbc_account", sets, "mobile = ?", mobile);
        return LbcUser.builder()
                .mobile(mobile)
                .goldQty(MoneyKit.of(goldAccount))
                .silverQty(MoneyKit.of(silverAccount))
                .build();
    }

    public synchronized void updateRepeatBuyJy(String mobile, boolean repeatBuy) {
        int newValue = repeatBuy ? 1 : 0;
        int targetOldValue = repeatBuy ? 0 : 1;
        JSONObject sets = new JSONObject().fluentPut("repeatBuyKj", newValue);
        if (repeatBuy) {
            sets.fluentPut("repeatBuyKjTime", new Date());
        }
        updateRow(jdbcTemplate, "t_lbc_account", sets, "mobile = ? and repeatBuyKj = ?", mobile, targetOldValue);
    }

    public void lottery(String mobile, Proxy proxy) {
        JSONObject row = getAndCheckRow(mobile);
        double goldAccount = row.getDoubleValue("goldAccount");
        int times = (int)goldAccount / 5;
        try {
            for (int i = 0; i < times; i++) {
                doLottery(mobile, proxy);
            }
        } finally {
            saveLotteryResult(mobile, proxy);
            getUserInfo(mobile, proxy);
        }
    }

    private void saveLotteryResult(String mobile, Proxy proxy) {
        String text = get2Text(mobile, "https://www.bluebeicoin.com/turntable_list", proxy);
        String content = Jsoup.parse(text)
                .select(".zhongjiang_table")
                .first()
                .html();
        JSONObject row = new JSONObject()
                .fluentPut("mobile", mobile)
                .fluentPut("lottery_list", content);
        insertRow(jdbcTemplate, "t_lbc_lottery", row);
    }

    private void doLottery(String mobile, Proxy proxy) {
        String text = get2Text(mobile, "https://www.bluebeicoin.com/turntable", proxy);
        String token = StringKit.extract(text, "_token:\"([0-9A-Za-z]+)\"");
        post2Json(mobile, "https://www.bluebeicoin.com/get_rand", new JSONObject().fluentPut("_token", token), proxy);
    }


    public void buyKj4CPValue(String mobile, double targetCPValue, Proxy proxy) {
        JSONObject row = getAndCheckRow(mobile);
        double cpValue = row.getDoubleValue("cpValue");
        if (cpValue >= targetCPValue) {
            updateRepeatBuyJy(mobile, false);
            return;
        }

        for (;;) {
            getUserInfo(mobile, proxy);

            cpValue = getCPValue(mobile, proxy);
            if (cpValue >= targetCPValue) {
                updateRepeatBuyJy(mobile, false);
                return;
            }

            row = getAndCheckRow(mobile);
            double total = row.getDoubleValue("goldAccount") + row.getDoubleValue("silverAccount");
            if (total < 10) {
                return;
            }

            int times = (int)((targetCPValue - cpValue) / 0.01);
            int maxTimes = (int) total / 10;

            for (int i = 0; i < Math.min(times, maxTimes); i++) {
                try {
                    buySmallKJ(mobile, proxy);
                } catch (BizException e) {
                    if (e.getMessage() != null && e.getMessage().contains("账户余额不足")) {
                        break;
                    }
                    throw e;
                }
            }
            getMyKjs(mobile, proxy);
        }
    }

    public double getCPValue(String mobile, Proxy proxy) {
        String indexHtml = get2Text(mobile, "https://www.bluebeicoin.com/index", proxy);
        String str = Jsoup.parse(indexHtml)
                .select(".shou_suanli .shou_suli")
                .first()
                .toString();
        double cpValue = NumberUtils.toDouble(StringKit.extract(str, ">(([0-9\\.]+))<"));

        updateRow(jdbcTemplate, "t_lbc_account",  new JSONObject().fluentPut("cpValue", cpValue), "mobile=?", mobile);
        return cpValue;
    }

    private void buySmallKJ(String mobile, Proxy proxy) {
        String token = getHtmlToken(mobile, "https://www.bluebeicoin.com/market/miner/1", proxy);
        JSONObject resJson = post2Json(mobile, "https://www.bluebeicoin.com/market/miner/1", new JSONObject().fluentPut("_token", token), proxy);
        checkResJson(resJson);
    }


    public List<JSONObject> listMine() {
        return queryForRows(jdbcTemplate, "select * from t_lbc_account where mine = 1 and worked = 1");
    }

    /**
     * 求购列表 从大到小
     */
    public List<LbcJy> getBuyJyList(String mobile, Proxy proxy) {
        JSONObject row = getAndCheckRow(mobile);
        if (!"success".equals(row.getString("idStatus"))) {
            throw BizException.messageOf("{} 未实名通过", mobile);
        }
        String token = getHtmlToken(mobile, "https://www.bluebeicoin.com/deal", proxy);

        String rText = get2Text(mobile, "https://www.bluebeicoin.com/deal/list", proxy);
        JSONObject retJson = JSON.parseObject(rText);
        checkResJson(retJson);

        JSONArray dataJsonArr = retJson.getJSONObject("data").getJSONArray("data");
        if (dataJsonArr.isEmpty()) {
            return Lists.newArrayList();
        }
        List<LbcJy> result = Lists.newArrayList();
        for (Object obj : dataJsonArr) {
            JSONObject jsonObj = (JSONObject) obj;
            BigDecimal amount = MoneyKit.of(jsonObj.getDouble("total"));
            Integer qty = jsonObj.getInteger("num");
            String orderId = jsonObj.getString("order");
            BigDecimal price = MoneyKit.of(jsonObj.getDouble("price"));
            result.add(
                    LbcJy.builder()
                            .amount(amount)
                            .qty(qty)
                            .price(price)
                            .jyId(orderId)
                            .build()
            );
        }
        return result.stream()
                .sorted(Comparator.comparing(LbcJy::getJyId).reversed())
                .collect(Collectors.toList());
    }

    private synchronized void newJy(String owner, String type, String buyerMobile, String sellerMobile, Proxy proxy) {

        checkOwner(owner);
        checkIsMobile(buyerMobile);
        checkIsMobile(sellerMobile);

        if (isNotJyTime()) {
            throw BizException.messageOf("当前不在交易时间内");
        }
        if (buyerMobile.equals(sellerMobile)) {
            throw BizException.messageOf("卖出和买入的账号不能一致");
        }
//        if (jdbcTemplate.queryForObject("select count(1) from v_stl_can_not_sell_now where mobile = ?", int.class, sellerMobile) > 0) {
//            throw BizException.messageOf("{} 今日已有卖出订单，无法再卖出了", sellerMobile);
//        }

        List<LbcJy> buyJyList = getBuyJyList(sellerMobile, proxy);
        if (buyJyList == null || buyJyList.isEmpty()) {
            throw BizException.messageOf("无求购订单，建议暂时不要交易");
        }
        LbcUser sellerLbcUser = getUserInfo(sellerMobile, proxy);
        Integer jyQty = calcSellerMaxJyQty(sellerLbcUser);

        String token = getHtmlToken(buyerMobile, "https://www.bluebeicoin.com/deal", proxy);
        JSONObject resJson = null;
        try {
            //如果timeout也要处理这种情况 todo
            resJson = post2Json(buyerMobile, "https://www.bluebeicoin.com/deal/create",
                    new JSONObject()
                            .fluentPut("_token", token)
                            .fluentPut("num", jyQty)
                            .fluentPut("price", buyJyList.get(0).getPrice().doubleValue())
                    , proxy);
            checkResJson(resJson);
        } catch (Exception e) {
            logger.error("创建订单失败", e);
        }

        LbcOrder order = getBuyIngOrders(buyerMobile, proxy).get(0);
        if (order == null) {
            throw BizException.messageOf("查询订单失败");
        }
        String jyId = order.getJyId();
        try {
            JSONObject newRow = new JSONObject()
                    .fluentPut("jyId", jyId)
                    .fluentPut("status", "New")
                    .fluentPut("buyerMobile", buyerMobile)
                    .fluentPut("qty", jyQty)
                    .fluentPut("amount", order.getAmount())
                    .fluentPut("jyStatus", order.getJyStatus())
                    .fluentPut("owner", owner)
                    .fluentPut("type", type)
                    .fluentPut("resError", resJson.getString("status"))
                    .fluentPut("resText", resJson.toJSONString());
            insertRow(jdbcTemplate, "t_lbc_jy", newRow);
            this.buyJy(jyId, sellerMobile, proxy);
        } catch (Exception e) {
            this.cancelJy(buyerMobile, jyId, proxy);
            throw e;
        }
    }

    public void newSysJy(String owner, String buyerMobile, Proxy proxy) {
        checkOwner(owner);
//        JSONObject sellerRow = queryForRow(jdbcTemplate, "select * from t_lbc_account where worked = 1 " +
////                " and isMyAccount = 0 " +
////                " and mobile not in (select mobile from v_lbc_can_not_sell_now) " +
//                " and goldAccount >= ?" +
//                " order by updateTime asc limit 1", 10);
//        if (sellerRow == null) {
//            throw BizException.messageOf("无可售小号");
//        }
//        String sellerMobile = sellerRow.getString("mobile");
        String sellerMobile = "15279942428";
        this.newJy(owner, "4NextDaySell", buyerMobile, sellerMobile, proxy);
    }

    private Integer calcSellerMaxJyQty(LbcUser lbcUser) {
        int goldQty = lbcUser.getGoldQty().intValue();
        if (goldQty >= 10) {
//            return goldQty; //todo
            return 10;
        } else if (goldQty >= 200) {
            return 200;
        } else {
            throw BizException.messageOf("计算交易数据错误");
        }
    }

    public List<LbcOrder> getBuyIngOrders(String mobile, Proxy proxy) {
        String dealHtml = get2Text(mobile, "https://www.bluebeicoin.com/deal", proxy);
        Document root = Jsoup.parse(dealHtml);
        Elements items = root.select("#trade_list_box5 table tr");
        if (items.size() <= 1) {
            return Lists.newArrayList();
        }
        List<LbcOrder> orders = Lists.newArrayList();
        // 等待卖家接单 买家付款 上传图片
        for (int i = 1; i < items.size(); i++) {
            Element element = items.get(i);
            Elements tdEle = element.getElementsByTag("td");
            String jyId = tdEle.get(0).text();
            Integer qty = Integer.valueOf(tdEle.get(1).text().trim());
            Double price = Double.valueOf(tdEle.get(2).text().trim());
            String jyStatus = tdEle.get(3).text().trim();
            BigDecimal amount = BigDecimalKit.of(price * qty);
            LbcOrder order = LbcOrder.builder()
                    .jyId(jyId)
                    .qty(qty)
                    .amount(amount)
                    .jyStatus(jyStatus)
                    .build();
            orders.add(order);
        }
        return orders;
    }

//    private List<LbcOrder> getSellerIngOrders(String mobile, boolean proxy) {
//        String dealHtml = get2Text(mobile, "https://www.bluebeicoin.com/deal", proxy);
//        Document root = Jsoup.parse(dealHtml);
//        Elements items = root.select("#trade_list_box4 table tr");
//        if (items.size() <= 1) {
//            return Lists.newArrayList();
//        }
//        List<LbcOrder> orders = Lists.newArrayList();
//        // 等待中 买家已付款 确认完成
//        for (int i = 1; i < items.size(); i++) {
//            Element element = items.get(i);
//            Elements tdEle = element.getElementsByTag("td");
//            LbcOrder order = LbcOrder.builder()
//                    .jyId(tdEle.get(0).select("div input").attr("data-order"))
//                    .jyStatus(tdEle.get(2).text())
//                    .qty(Integer.valueOf(tdEle.get(3).text()))
//                    .amount(BigDecimalKit.of(tdEle.get(4).text()))
//                    .build();
//            orders.add(order);
//        }
//        return orders;
//    }

    /**
     * New 新增交易
     * Waiting_Buyer_Pay 等待买家付款
     * Waiting_Seller_Confirm 等待卖家确认
     * Success 交易完成（成功）
     */
    /**
     * 卖家，买币
     */
    private void buyJy(String jyId, String sellerMobile, Proxy proxy) {

        JSONObject jyRow = queryForRow(jdbcTemplate, "select * from t_lbc_jy where jyId = ?", jyId);
        String buyerMobile = jyRow.getString("buyerMobile");
        String status = jyRow.getString("status");
        if (StringKit.notEqualsIgnoreCase("New", status)) {
            throw BizException.messageOf("状态异常");
        }

        String token = getHtmlToken(sellerMobile, "https://www.bluebeicoin.com/deal", proxy);

        JSONObject resJson = post2Json(sellerMobile, "https://www.bluebeicoin.com/deal/accept",
                new JSONObject()
                        .fluentPut("_token", token)
                        .fluentPut("order", jyId),
                proxy);
        try {
            checkResJson(resJson);
        } catch (BizException e) {
//            String message = e.getMessage();
//            if (message.contains("抢先一步")) {
//                StlJyDetail jyDetail = this.getJyShowAsBuyer(buyerMobile, jyId);
//                if (sellerMobile.equalsIgnoreCase(jyDetail.getSellerMobile())) {
//                    // is okay
//                } else {
//                    throw e;
//                }
//            } else {
            throw e;
//            }
        }
        jdbcTemplate.update("update t_lbc_jy set status = 'Waiting_Buyer_Pay', sellerMobile = ? where jyId = ?", sellerMobile, jyId);
    }

    public void cancelJy(String mobile, String jyId, Proxy proxy) {
        String token = getHtmlToken(mobile, "https://www.bluebeicoin.com/deal", proxy);
        JSONObject resJson = post2Json(mobile, "https://www.bluebeicoin.com/deal/cancel",
                new JSONObject()
                        .fluentPut("_token", token)
                        .fluentPut("order", jyId),
                proxy);
        checkResJson(resJson);
        jdbcTemplate.update("update t_lbc_jy set status = 'Cancelled' where jyId = ?", jyId);
    }

    public File jieTu(String jyId, boolean refresh) {
        File imgFile = new File(LbcService.JIE_TU_DIR, "lbc-jy-" + jyId + ".png");
        if (!imgFile.exists() || refresh) {
            return newJieTu(jyId);
        }
        return imgFile;
    }

    private File newJieTu(String jyId) {
        File file = jieTuService.newJieTu("http://localhost:8080/jietu/lbc/alipay?jyId=" + jyId, "lbc-jy-" + jyId);
        try {
            FileUtils.copyFileToDirectory(file, JieTuService.JIE_TU_DIR);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return new File(JieTuService.JIE_TU_DIR, "lbc-jy-" + jyId + ".png");
    }

    public void payJyWithAutoJieTu(String jyId, Proxy proxy) {
        try {
            File imgFile = this.jieTu(jyId, false);
            this.payJy(jyId, imgFile, proxy);
        } catch (JSONException e) {
//            if (e.getMessage().contains("imagecreatefrompng()")) {
//                notificationService.syncEmail("截图上传异常，正在重新生成截图");
//                jieTu(jyId, true);
//            } else {
            throw e;
//            }
        }
    }

    /**
     * 买家，点已付款  zfjy 支付交易
     */
    public void payJy(String jyId, File payImg, Proxy proxy) {
        JSONObject jyRow = queryForRow(jdbcTemplate, "select * from t_lbc_jy where jyId = ?", jyId);
        String buyerMobile = jyRow.getString("buyerMobile");
        String status = jyRow.getString("status");
        if (StringKit.notEqualsIgnoreCase("Waiting_Buyer_Pay", status)) {
            throw BizException.messageOf("状态异常");
        }
        if (!payImg.exists()) {
            throw BizException.messageOf("文件不存在");
        }
        String uri = this.uploadFile(buyerMobile, jyId, payImg, proxy);

        String token = getHtmlToken(buyerMobile, "https://www.bluebeicoin.com/deal", proxy);
        JSONObject resJson = post2Json(buyerMobile, "https://www.bluebeicoin.com/deal/img_upload",
                new JSONObject()
                        .fluentPut("_token", token)
                        .fluentPut("img", uri)
                        .fluentPut("order", jyId),
                proxy);
        checkResJson(resJson);
        jdbcTemplate.update("update t_lbc_jy set status = 'Waiting_Seller_Confirm', payImgUrl = ? where buyerMobile = ? and jyId = ?", uri, buyerMobile, jyId);
    }

    public String uploadFile(String mobile, String jyId, File file, Proxy proxy) {

        if (file == null || !file.exists()) {
            throw BizException.messageOf("上传的图片不存在");
        }
        JSONObject row = getAndCheckRow(mobile);

        String token = getHtmlToken(mobile, "https://www.bluebeicoin.com/deal", proxy);

        FilePart filePart = FilePart.builder()
                .name("filename")
                .contentType("image/jpeg")
                .filename(UUID.randomUUID().toString().toUpperCase() + ".jpg")
                .file(file)
                .build();
        JSONObject resJson = MyHttpRequest.post(getHttpClient(mobile, proxy), "https://www.bluebeicoin.com/upload/deals")
                .param("_token", token)
                .param("order", jyId)
                .part(filePart)
                .executeUtilReturnAndWaitMillSecond(REQUEST_MILLSECOND_INTERVAL, 200)
                .asJson();
        return resJson.getJSONObject("data").getString("file");
    }

    /**
     * 卖家，收到钱，完成，确认交易
     */
    public synchronized void confirmJy(String jyId, Proxy proxy) {
        checkNotNull("jyId", jyId);
        JSONObject jyRow = queryForRow(jdbcTemplate, "select * from t_lbc_jy where jyId = ?", jyId);
        String status = jyRow.getString("status");
        if (StringKit.notEqualsIgnoreCase("Waiting_Seller_Confirm", status)) {
            throw BizException.messageOf("状态异常");
        }
        String sellerMobile = jyRow.getString("sellerMobile");
        String buyerMobile = jyRow.getString("buyerMobile");
        doConfirmJy(sellerMobile, jyId, proxy);
        jdbcTemplate.update("update t_stl_jy set status = 'Success' where jyId = ?", jyId);
        safeRun(() -> {
            this.getUserInfo(sellerMobile, proxy);
            this.getUserInfo(buyerMobile, proxy);
        });
    }

    private void doConfirmJy(String mobile, String jyId, Proxy proxy) {
        JSONObject row = getAndCheckRow(mobile);
        String token = getHtmlToken(mobile, "https://www.bluebeicoin.com/deal", proxy);
        JSONObject resJson = post2Json(mobile, "https://www.bluebeicoin.com/deal/confirm",
                new JSONObject()
                        .fluentPut("_token", token)
                        .fluentPut("order", jyId)
                        .fluentPut("paypass", row.getString("payPassword")),
                proxy);
        try {
            checkResJson(resJson);
        } catch (BizException e) {
//            if (e.getMessage().contains("该交易已经确认了")) {
//                // ok
//            } else {
            throw e;
//            }
        }
    }

}
